package services

import (
	"bsm/daos"
	"bsm/logger"
	"bsm/models"
	"bsm/utils"
	"crypto/md5"
	"database/sql"
	"fmt"
	"strconv"
	"strings"

	"github.com/google/uuid"
)

type ImportService interface {
	ImportBudgetSheet(file string, designtype int, uid string) error
	AddUser(uname string, org models.Org, urole string) (*models.ProjectUser, error)
	CheckBudgetExit(prjcode string) (bool, error)
}

type importServiceImpl struct {
	importDao daos.ImportDao
	userDao   daos.UserDao
}

func NewImportService(importDao daos.ImportDao, userDao daos.UserDao) ImportService {
	return &importServiceImpl{
		importDao: importDao,
		userDao:   userDao,
	}
}

func (pis *importServiceImpl) ImportBudgetSheet(file string, designtype int, uid string) error {

	var project models.ImportProject
	xt := utils.XlsxTools{}
	err := xt.NewXlsxTools(file, designtype)
	if err != nil {
		logger.WithFields(map[string]interface{}{
			"layer":     "service",
			"operation": "importServiceImpl.ImportBudgetSheet",
		}).Errorf("打开文件失败：%v", err)
		return fmt.Errorf("打开文件失败：%w", err)
	}

	err = pis.importGeneralInfo(xt, &project, designtype)
	if err != nil {
		return fmt.Errorf("获取综合信息数据失败: %w", err)
	}
	err = pis.importLaborInfo(xt, &project, designtype)
	if err != nil {
		return fmt.Errorf("获取人工成本数据失败: %w", err)
	}
	err = pis.importMaterialInfo(xt, &project, designtype)
	if err != nil {
		return fmt.Errorf("获取材料成本数据失败: %w", err)
	}
	err = pis.importOtherfeeInfo(xt, &project, designtype)
	if err != nil {
		return fmt.Errorf("获取其他成本数据失败: %w", err)
	}

	xt.File.Close()

	// 删除导入的文件
	// if err := os.Remove(file); err != nil {
	// 	logger.WithFields(map[string]interface{}{
	// 		"layer":     "service",
	// 		"operation": "importServiceImpl.ImportBudgetSheet",
	// 	}).Errorf("删除导入文件失败：%v", err)
	// 	return fmt.Errorf("删除文件失败：%w", err)
	// }

	err = pis.importDao.InTransaction(func() error {
		if designtype == models.DesignType_Budget {
			if err := pis.importDao.ImportBase(project.ImportBase, uid); err != nil {
				return fmt.Errorf("import base failed: %w", err)
			}
		}
		if err := pis.importDao.ImportLabor(project.LaborItems); err != nil {
			return fmt.Errorf("import labor failed: %w", err)
		}
		if err := pis.importDao.ImportMaterial(project.MaterialItems); err != nil {
			return fmt.Errorf("import material failed: %w", err)
		}
		if err := pis.importDao.ImportOther(project.OtherItems); err != nil {
			return fmt.Errorf("import other failed: %w", err)
		}

		if err := pis.importDao.SetProjectState(project.DesignCode, project.ProjectState); err != nil {
			return fmt.Errorf("set project state failed: %w", err)
		}

		return nil
	})

	if err != nil {
		logger.WithFields(map[string]interface{}{
			"layer":      "service",
			"operation":  "ImportBudgetSheet",
			"designcode": project.GeneralInfo.DesignCode,
			"designtype": xt.ProjectState,
		}).Errorf("import project failed: %v", err)
	} else {
		logger.WithFields(map[string]interface{}{
			"layer":      "service",
			"operation":  "ImportBudgetSheet",
			"designcode": project.GeneralInfo.DesignCode,
			"designtype": xt.ProjectState,
		}).Infof("import project success")
	}
	return err
}

// importGeneralInfo 导入综合信息
func (pis *importServiceImpl) importGeneralInfo(xt utils.XlsxTools, project *models.ImportProject, designtype int) error {

	var gi models.GeneralInfo
	if err := xt.ReadSingleRecord(&gi); err != nil {
		logger.Errorf("导入时读取综合信息失败:%v", err)
		return fmt.Errorf("读取综合信息数据失败：%w", err)
	}

	project.GeneralInfo = gi
	project.ProjectState = designtype

	projectState, err := pis.importDao.GetProjectState(gi.DesignCode)
	if err != nil {
		logger.WithFields(map[string]interface{}{
			"layer":     "service",
			"operation": "importServiceImpl.ImportBudgetSheet",
		}).Errorf("获取项目状态失败：%v", err)
		return fmt.Errorf("获取项目状态失败")
	}

	if projectState >= designtype {
		logger.WithFields(map[string]interface{}{
			"layer":     "service",
			"operation": "importServiceImpl.ImportBudgetSheet",
		}).Errorf("项目状态已存在：%s", gi.DesignCode)
		msg := models.ProjectStates[designtype]
		return fmt.Errorf("项目%s数据已存在，无法导入", msg)
	}

	if designtype != models.DesignType_Budget { // 非预算设计导入时，先检查项目预算是否存在
		budgetExit, err := pis.CheckBudgetExit(gi.DesignCode)
		if err != nil {
			logger.Errorf("无法确定项目预算是否存在：%v", err)
			return fmt.Errorf("检查项目预算设计是否存在失败")
		}
		if !budgetExit {
			if designtype != models.DesignType_Budget {
				logger.Errorf("项目预算设计不存在：%s", gi.DesignCode)
				return fmt.Errorf("预算设计不存在，无法导入结算")
			}
		}
	}
	org, err := pis.importDao.GetOrgByProjectCode(gi.DesignCode)
	if err != nil {
		logger.Errorf("获取建设单位编码失败：%v", err)
		return fmt.Errorf("获取建设单位编码失败")
	}
	project.OrgCode = org.OrgCode

	designerID, err := pis.importDao.GetUserIdByName(org.OrgCode, gi.Designer)
	if err != nil {
		logger.Warnf("获取设计人编码失败,添加设计人。err：%v", err)
		pd, err := pis.AddUser(gi.Designer, org, models.UserRole_Designer)
		if err != nil {
			logger.Errorf("添加设计人失败：%v", err)
			return fmt.Errorf("添加设计人失败")
		}
		designerID = pd.UserID
	}
	project.GeneralInfo.Designer = designerID

	reviewerID, err := pis.importDao.GetUserIdByName(org.OrgCode, gi.Reviewer)
	if err != nil {
		logger.Warnf("获取审核人编码失败,添加审核人。err：%v", err)
		pr, err := pis.AddUser(gi.Reviewer, org, models.UserRole_DesignReviewer)
		if err != nil {
			logger.WithFields(map[string]interface{}{
				"layer":     "service",
				"operation": "importServiceImpl.ImportBudgetSheet",
			}).Errorf("添加审核人失败：%v", err)
			return fmt.Errorf("添加审核人失败")
		}
		reviewerID = pr.UserID
	}
	project.GeneralInfo.Reviewer = reviewerID

	switch designtype {
	case models.DesignType_Budget:
		project.TimeBudget = sql.NullTime{Time: gi.PreparationDate.Time, Valid: true}
	case models.DesignType_Settlement:
		project.TimeSettle = sql.NullTime{Time: gi.PreparationDate.Time, Valid: true}
	case models.DesignType_Audit:
		project.TimeAudit = sql.NullTime{Time: gi.PreparationDate.Time, Valid: true}
	}

	return nil
}

func (pis *importServiceImpl) importLaborInfo(xt utils.XlsxTools, project *models.ImportProject, designtype int) error {
	// 处理表三数据
	tab3 := "（表三）甲"
	tabIndex, err := xt.File.GetSheetIndex(tab3)
	if err != nil || tabIndex < 0 {
		logger.WithFields(map[string]interface{}{
			"layer":     "service",
			"operation": "importServiceImpl.ImportBudgetSheet",
		}).Errorf("获取表三失败：%v", err)
		return fmt.Errorf("获取表三失败")
	}

	var tab3Data []models.ImportLaborItem

	startRow := 6
	for i := startRow; i < 1000; i++ {
		str, err := xt.File.GetCellValue(tab3, "B"+strconv.Itoa(i))
		if str == "" || err != nil {

			break
		}
		var tab models.ImportLaborItem

		tab.LID = uuid.New().String()

		tab.DesignCode = project.GeneralInfo.DesignCode
		tab.DesignType = designtype
		tab.Code = str

		str, err = xt.File.GetCellValue(tab3, "E"+strconv.Itoa(i))
		if err != nil {
			logger.WithFields(map[string]interface{}{
				"layer":     "service",
				"operation": "importServiceImpl.ImportBudgetSheet",
			}).Errorf("获取定额数量项目失败：%v", err)
			return fmt.Errorf("获取项目定额数量失败")
		}
		tab.Quantity, err = strconv.ParseFloat(strings.TrimSpace(str), 64)
		if err != nil {
			logger.WithFields(map[string]interface{}{
				"layer":     "service",
				"operation": "importServiceImpl.ImportBudgetSheet",
			}).Errorf("获取定额数量项目失败：%v", err)
			return fmt.Errorf("获取项目定额数量失败")
		}

		tab3Data = append(tab3Data, tab)
	}

	project.LaborItems = tab3Data
	return nil
}

func (pis *importServiceImpl) importMaterialInfo(xt utils.XlsxTools, project *models.ImportProject, designtype int) error {
	// 处理表四数据
	var tab4Data []models.ImportMaterialItem
	tab4 := "表四"
	tabIndex, err := xt.File.GetSheetIndex(tab4)
	if err != nil || tabIndex < 0 {
		logger.WithFields(map[string]interface{}{
			"layer":     "service",
			"operation": "importServiceImpl.ImportBudgetSheet",
		}).Errorf("获取表四失败：%v", err)
		return fmt.Errorf("获取表四失败")
	}

	startRow := 7
	for i := startRow; i < 1000; i++ {
		str, err := xt.File.GetCellValue(tab4, "B"+strconv.Itoa(i))
		if str == "" || err != nil {

			break
		}
		var tab models.ImportMaterialItem
		tab.DesignCode = project.GeneralInfo.DesignCode
		tab.DesignType = designtype
		tab.Code = str

		str, err = xt.File.GetCellValue(tab4, "E"+strconv.Itoa(i))
		if err != nil {
			logger.WithFields(map[string]interface{}{
				"layer":     "service",
				"operation": "importServiceImpl.ImportBudgetSheet",
			}).Errorf("获取材料数量单元格失败：%v", err)
			return fmt.Errorf("获取材料数量失败")
		}
		tab.Quantity, err = strconv.ParseFloat(strings.TrimSpace(str), 64)

		if err != nil {
			logger.WithFields(map[string]interface{}{
				"layer":     "service",
				"operation": "importServiceImpl.ImportBudgetSheet",
			}).Errorf("材料数量单元格转换失败：%v", err)
			return fmt.Errorf("获取材料数量失败")
		}

		str, err = xt.File.GetCellValue(tab4, "F"+strconv.Itoa(i))
		if err != nil {
			logger.WithFields(map[string]interface{}{
				"layer":     "service",
				"operation": "importServiceImpl.ImportBudgetSheet",
			}).Errorf("获取材料价格单元格失败：%v", err)
			return fmt.Errorf("获取材料价格失败")
		}
		tab.Price, err = strconv.ParseFloat(strings.TrimSpace(str), 64)

		if err != nil {
			logger.WithFields(map[string]interface{}{
				"layer":     "service",
				"operation": "importServiceImpl.ImportBudgetSheet",
			}).Errorf("材料价格单元格转换失败：%v", err)
			return fmt.Errorf("获取材料价格失败")
		}

		tab4Data = append(tab4Data, tab)
	}

	project.MaterialItems = tab4Data
	return nil
}

func (pis *importServiceImpl) importOtherfeeInfo(xt utils.XlsxTools, project *models.ImportProject, designtype int) error {
	// 处理表五数据
	var tab5Data []models.ImportOtherItem
	tab5 := "表五"
	tabIndex, err := xt.File.GetSheetIndex(tab5)
	if err != nil || tabIndex < 0 {
		logger.WithFields(map[string]interface{}{
			"layer":     "service",
			"operation": "importServiceImpl.ImportBudgetSheet",
		}).Errorf("获取表五失败：%v", err)
		return fmt.Errorf("获取表五失败")
	}

	startRow := 25
	for i := startRow; i < 1000; i++ {
		str, err := xt.File.GetCellValue(tab5, "B"+strconv.Itoa(i))
		if str == "" || err != nil {
			break
		}

		var tab models.ImportOtherItem
		tab.DesignCode = project.GeneralInfo.DesignCode
		tab.DesignType = designtype
		tab.Name = str

		str, err = xt.File.GetCellValue(tab5, "A"+strconv.Itoa(i))
		if err != nil {
			logger.WithFields(map[string]interface{}{
				"layer":     "service",
				"operation": "importServiceImpl.ImportBudgetSheet",
			}).Errorf("读取表五序号单元格失败：%v", err)
			str = strconv.Itoa(i - 8) // 默认顺延表五模板序号

		}
		tab.SerialNumber = str

		str, err = xt.File.GetCellValue(tab5, "C"+strconv.Itoa(i))
		if err != nil {
			logger.WithFields(map[string]interface{}{
				"layer":     "service",
				"operation": "importServiceImpl.ImportBudgetSheet",
			}).Warnf("读取表五计算依据及方法单元格失败：%v", err)
		}
		tab.Basis = str

		str, err = xt.File.GetCellValue(tab5, "D"+strconv.Itoa(i))
		if err != nil {
			logger.WithFields(map[string]interface{}{
				"layer":     "service",
				"operation": "importServiceImpl.ImportBudgetSheet",
			}).Warnf("读取表五除税价单元格失败：%v，跳过此行", err)
			continue
		}
		tab.TaxExcluded, err = strconv.ParseFloat(strings.TrimSpace(str), 64)

		if err != nil {
			logger.WithFields(map[string]interface{}{
				"layer":     "service",
				"operation": "importServiceImpl.ImportBudgetSheet",
			}).Warnf("读取表五除税价单元格转换失败：%v，跳过此行", err)
			continue
		}

		str, err = xt.File.GetCellValue(tab5, "E"+strconv.Itoa(i))
		if err != nil {
			logger.WithFields(map[string]interface{}{
				"layer":     "service",
				"operation": "importServiceImpl.ImportBudgetSheet",
			}).Warnf("读取表五增值税单元格失败：%v，跳过此行", err)
			continue
		}
		tab.Tax, err = strconv.ParseFloat(strings.TrimSpace(str), 64)

		if err != nil {
			logger.WithFields(map[string]interface{}{
				"layer":     "service",
				"operation": "importServiceImpl.ImportBudgetSheet",
			}).Warnf("读取表五增值税单元格转换失败：%v，跳过此行", err)
			continue
		}

		str, err = xt.File.GetCellValue(tab5, "F"+strconv.Itoa(i))
		if err != nil {
			logger.WithFields(map[string]interface{}{
				"layer":     "service",
				"operation": "importServiceImpl.ImportBudgetSheet",
			}).Warnf("读取表五含税价单元格失败：%v，跳过此行", err)
			continue
		}
		tab.TaxIncluded, err = strconv.ParseFloat(strings.TrimSpace(str), 64)

		if err != nil {
			logger.WithFields(map[string]interface{}{
				"layer":     "service",
				"operation": "importServiceImpl.ImportBudgetSheet",
			}).Warnf("读取表五含税价单元格转换失败：%v，跳过此行", err)
			continue
		}

		tab.Remark, err = xt.File.GetCellValue(tab5, "G"+strconv.Itoa(i))
		if err != nil {
			logger.WithFields(map[string]interface{}{
				"layer":     "service",
				"operation": "importServiceImpl.ImportBudgetSheet",
			}).Warnf("读取表五备注单元格失败：%v", err)
		}

		tab5Data = append(tab5Data, tab)
	}
	project.OtherItems = tab5Data
	return nil
}

func (pis *importServiceImpl) AddUser(uname string, org models.Org, urole string) (*models.ProjectUser, error) {
	// 检查用户名是否已存在
	uid := utils.GetPinyin(uname)
	uidplus := uid
	for i := 1; i <= 3; i++ {
		_, err := pis.userDao.GetUserByUserID(uidplus)
		if err == nil {
			i++
			uidplus = uid + fmt.Sprintf("%d", i)
		} else {
			break
		}
	}

	var user models.ProjectUser
	user.UserID = uidplus
	user.UserName = uname
	hash_new := md5.Sum([]byte(user.UserID + "-" + "111111"))
	hashStr_new := fmt.Sprintf("%x", hash_new)
	user.Password = hashStr_new
	user.OrgCode = org.OrgCode
	user.OrgLevel = org.OrgLevel
	err := pis.userDao.AddUser(&user)
	if err != nil {
		logger.WithFields(map[string]interface{}{
			"layer":     "service",
			"operation": "userService-AddUser",
		}).Errorf("AddUser failed - username: %s, error: %s", uname, err)
		return nil, err
	}
	err = pis.userDao.AddUserRole(&models.UserRole{
		UserID: user.UserID,
		RoleID: urole,
	})
	if err != nil {
		logger.WithFields(map[string]interface{}{
			"layer":     "service",
			"operation": "userService-AddUser",
		}).Errorf("AddUserRole failed - userID: %s, roleID: %s, error: %s", user.UserID, urole, err)
		return nil, err
	}
	return &user, nil
}

func (pis *importServiceImpl) CheckBudgetExit(prjcode string) (bool, error) {
	return pis.importDao.CheckProjectExit(prjcode)
}
